#include "myslm.h"

namespace myslm {

static inline size_t ROUND(size_t x, size_t u) {
    return (x / u) * u;
}

thread_local static char local_buffer[4096];

// uint64_t miss = 0;

constexpr uint16_t SIMPLE_CRC = (uint16_t)0x5AB4;

void Serialize(ReqType op, _key_t k, const string & v, char * buffer) {
    Request * req = (Request *)buffer;
    req->op = op; 
    req->crc = SIMPLE_CRC; 
    req->val_size = v.size();
    *((uint64_t *)req->keyvalue) = k;
    memcpy(req->keyvalue + 8, v.c_str(), v.size());
}

bool Deserialize(char * buffer, std::string & out) {
    Request * req = (Request *)buffer;
    if(req->crc != SIMPLE_CRC) {
        return false;
    } else {
        out = std::string(req->keyvalue + 8, req->val_size);
        return true;
    }
}

MySLM::MySLM(const SLMOpt & opt) {
    if(!folder_exist(opt.dbdir.c_str())) {
        mkdir(opt.dbdir.c_str(), 0777);
    }
    if(!folder_exist(opt.nvmdir.c_str())) {
        mkdir(opt.nvmdir.c_str(), 0777);
    }
    global_index_ = new tlbtree::TLBtree(opt.nvmdir + "/tlbtree.pool", opt.nvmsize);
    std::string logname = opt.dbdir + "/myslm.log";
    log_fd_ = open(logname.c_str(), O_RDWR | O_CREAT, 0755);
    if(log_fd_ < 3) {
        perror("open log file error");
        exit(-1);
    }

    uint64_t fs = file_size(logname.c_str());
    log_offset_ = std::max(fs, (uint64_t)4096);
}

MySLM::~MySLM() {
    delete global_index_;
}

void MySLM::Put(const _key_t k, const string & v, bool sync) {
    Serialize(PUT, k, v, local_buffer);
    Request * req = (Request *) local_buffer;
    uint64_t addr = Append(req, sync);
    global_index_->insert(k, addr);
}

bool MySLM::Update(const _key_t k, const string & v, bool sync) {
    Serialize(UPDATE, k, v, local_buffer);
    Request * req = (Request *) local_buffer;
    uint64_t addr = Append(req, sync);

    // update global index
    uint64_t old = global_index_->update(k, addr);
    if(old == 0) // not found in database 
        return false;
    else 
        return true;
    return true;
}

bool MySLM::Delete(const _key_t k, bool sync) {
    Serialize(PUT, k, "", local_buffer);
    Request * req = (Request *) local_buffer;
    Append(req, sync);
    uint64_t old = global_index_->remove(k);
    if(old == 0) // not found in database 
        return false;
    else 
        return true;
}

bool MySLM::Get(const _key_t k, string & v) {
    uint64_t addr = global_index_->lookup(k); // 通过全局索引找到数据
    assert(addr < log_offset_);
    auto discard = pread(log_fd_, local_buffer, 4096, addr);
    bool valid = Deserialize(local_buffer, v);
    return valid;
}

int MySLM::Scan(const _key_t k, int len, vector<string> & out) {
    vector<tlbtree::Record> full_res;
    global_index_->scan(k, len, full_res);


    for(int i = 0; i < full_res.size(); i++) {
        out.push_back(string());
        uint64_t addr = (uint64_t)full_res[i].val;
        
        auto discard = pread(log_fd_, local_buffer, 4096, addr);
        bool valid = Deserialize(local_buffer, out.back());
    }
    return out.size();
}

uint64_t MySLM::Append(Request * req, bool sync) {
    uint64_t addr = __sync_fetch_and_add(&log_offset_, req->Length());
    auto discard = pwrite(log_fd_, req, req->Length(), addr);
    if(sync) fsync(log_fd_);
    return addr;
}

} // namespace dnskv